//
//  BenRefreshableScrollView.swift
//  BenRefreshScroll
//
//  Created by Ben on 2022/3/1.
//

import SwiftUI
import Combine

public typealias BenRefreshComplete = () -> Void
public typealias BenOnRefresh = (@escaping BenRefreshComplete) -> Void

struct BenRefreshableScrollView<Content: View>: View {
    
    @StateObject private var refreshVM: BenRefreshableViewModel = BenRefreshableViewModel()



    let content: () -> Content
    let onRefreshHeader: BenOnRefresh
    let onRefreshFooter: BenOnRefresh
    
    init(@ViewBuilder content: @escaping () -> Content, onRefreshHeader: @escaping BenOnRefresh, onRefreshFooter: @escaping BenOnRefresh) {
        self.content = content
        self.onRefreshHeader = onRefreshHeader
        self.onRefreshFooter = onRefreshFooter
    }
    
    var body: some View {
        
        ScrollView(.vertical) {
            
            ZStack(alignment: .bottom) {
                VStack(spacing: 0) {
                    
                    GeometryReader { proxy -> AnyView in
                        let minY = proxy.frame(in: .global).minY
                        if refreshVM.normalOffsetY == 0 {
                            refreshVM.normalOffsetY = minY
                        }
                        DispatchQueue.main.async {
                            refreshVM.currOffsetY = minY
                            
                            let _refreshFooterCurrHeight = refreshVM.normalOffsetY - minY + refreshVM.scrollHeight - refreshVM.scrollContentHeight
                            
                            
                            if _refreshFooterCurrHeight > 0 && refreshVM.state != .refreshFooter {
                                refreshVM.refreshFooterCurrHeight = _refreshFooterCurrHeight
                            }
                            
                            if _refreshFooterCurrHeight > refreshVM.progressViewHeight && refreshVM.state != .pullUp && refreshVM.state == .normal {
                                
                                withAnimation {
                                    refreshVM.state = .pullUp
                                }
                            }
                            if _refreshFooterCurrHeight < refreshVM.progressViewHeight && refreshVM.state == .pullUp {
                                withAnimation {
                                    print("hello")
                                    refreshVM.state = .refreshFooter
                                }
                            }
                            
                            
                            if refreshVM.currOffsetY - refreshVM.normalOffsetY > refreshVM.progressViewHeight && refreshVM.state == .normal {
                                withAnimation(.easeIn) {
                                    refreshVM.state = .pullDown
                                }
                            }
                        }
                        
                        return AnyView(Color.clear)
                    }
                    .frame(height: 0)
                    
                    VStack(spacing: 0) {
                        
                        VStack {
                            Spacer(minLength: 0)
                            
                            if refreshVM.state == .refreshHeader {
                                ProgressView()
                                    .frame(height: refreshVM.progressViewHeight)
                            } else {
                                Image(systemName: "arrow.down")
                                    .frame(height: refreshVM.progressViewHeight)
                                    .rotationEffect(.init(degrees: refreshVM.state == .normal ? 0 : 180))
                                    .opacity(refreshVM.refreshHeaderCurrHeight == 0 ? 0 : 1)
                            }
                        }
                        .frame(height: refreshVM.refreshHeaderCurrHeight)
                        .clipped()
                        
                        content()
                    }
                    .overlay(
                        
                        GeometryReader { proxy -> AnyView in
                            
                            let height = proxy.frame(in: .global).height
                            
                            DispatchQueue.main.async {
                                if refreshVM.scrollContentHeight != height {
                                    refreshVM.scrollContentHeight = height
                                    print(refreshVM.scrollContentHeight)
                                }
                            }
                            
                            return AnyView(Color.clear)
                        }
                    )
                }
                .offset(y: refreshVM.state == .refreshFooter ? -refreshVM.progressViewHeight : 0)
                
                VStack {
                    
                    
                    if refreshVM.state == .refreshFooter {
                        ProgressView()
                            .frame(height: refreshVM.progressViewHeight)
                    } else {
                        Image(systemName: "arrow.up")
                            .frame(height: refreshVM.progressViewHeight)
                            .rotationEffect(.init(degrees: refreshVM.state == .normal ? 0 : 180))
                            .opacity(refreshVM.refreshFooterCurrHeight == 0 ? 0 : 1)
                    }
                    
                    Spacer(minLength: 0)
                }
                .frame(height: refreshVM.state == .refreshFooter ? refreshVM.progressViewHeight : refreshVM.refreshFooterCurrHeight)
                .frame(maxWidth: .infinity)
                .background(Color.orange)
                .clipped()
                .offset(y: refreshVM.state == .refreshFooter ? 0 : refreshVM.refreshFooterCurrHeight)
                .zIndex(1)
               
            }
            
            
        }
        .overlay(
            
            GeometryReader { proxy -> AnyView in
                
                let height = proxy.frame(in: .global).height
                
                DispatchQueue.main.async {
                    if refreshVM.scrollHeight != height {
                        refreshVM.scrollHeight = height
                    }
                }
                
                return AnyView(Color.clear)
            }
        )
        .onChange(of: refreshVM.state) { newValue in
            
            if newValue == .refreshHeader {
                
                onRefreshHeader {
                    DispatchQueue.main.async {
                        withAnimation {
                            refreshVM.state = .normal
                        }
                    }
                }
            }
            
            if newValue == .refreshFooter {
                
                onRefreshFooter {
                    DispatchQueue.main.async {
                        withAnimation {
                            refreshVM.state = .normal
                        }
                    }
                }
            }
        }
        
    }
    
}
